package mount

import (
	"bytes"
	"context"
	"fmt"
	"github.com/teris-io/shortid"
	"go.uber.org/zap"
	"os"
	"os/exec"
	"strings"
	"time"
	"virus-proxy/internal/logger"
)

// PREX 挂载路径前缀
const PREX = "/mnt/QiAnXin_"

var _ DiskOperator = (*LinuxDiskOperator)(nil)

type LinuxDiskOperator struct {
	Username string
	Password string
}

func NewLinuxDisk(username, password string) *LinuxDiskOperator {
	return &LinuxDiskOperator{Username: username, Password: password}
}

func (o *LinuxDiskOperator) FSMount(fsPath, username, passwd string) (err error) {
Generate:
	id, _ := shortid.Generate()
	mountPoint := PREX + id

	// 判断DriversMap的值是否等于mountPoint
	MountSliceIns.Mutex.RLock()
	for _, v := range MountSliceIns.DriversMap {
		if v == mountPoint {
			MountSliceIns.Mutex.RUnlock()
			goto Generate
		}
	}
	MountSliceIns.Mutex.RUnlock()

	err = os.Mkdir(mountPoint, 0655)
	if err != nil {
		logger.LogError("mkdir failed", zap.String("dir", mountPoint), zap.Error(err))
		return err
	}
	var cmd *exec.Cmd
	// 添加超时
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if o.Username != "" {
		cmd = exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("mount -t cifs -o username=%s,password=%s %s %s ", o.Username, o.Password, fsPath, mountPoint))
		//logger.LogDebug("mount", zap.String("mount verbose", fmt.Sprintf("mount -t cifs -o username=%s,password=%s %s %s ", o.Username, o.Password, fsPath, mountPoint)))
	} else {
		cmd = exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("mount -t cifs -o username=root,password= %s %s ", fsPath, mountPoint))
		//logger.LogDebug("mount", zap.String("mount verbose", fmt.Sprintf("mount -t cifs -o username=root,password= %s %s ", fsPath, mountPoint)))
	}
	output, err := cmd.CombinedOutput()
	if err != nil {
		_ = os.Remove(mountPoint)
		logger.LogError("mount failed", zap.String("fsPath", fsPath), zap.String("mountPoint", mountPoint))
		return fmt.Errorf("%s", output)
	}
	logger.LogInfo("mount success", zap.String("fsPath", fsPath), zap.String("mountPoint", mountPoint))
	MountSliceIns.Mutex.Lock()
	MountSliceIns.DriversMap[fsPath] = mountPoint
	MountSliceIns.Mutex.Unlock()
	return nil
}
func (o *LinuxDiskOperator) UnMountFS(fsPath, clientIp string) (err error) {
	fsPath = bytes.NewBufferString("//" + clientIp + fsPath).String()
	_ = UpdateMount()
	var mountPoint string
	var ok bool
	MountSliceIns.Mutex.RLock()
	if mountPoint, ok = MountSliceIns.DriversMap[fsPath]; !ok {
		MountSliceIns.Mutex.RUnlock()
		return fmt.Errorf("%s not mount", fsPath)
	}
	MountSliceIns.Mutex.RUnlock()
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	cmd := exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("umount %s", mountPoint))
	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("%s", output)
	}
	_ = os.Remove(mountPoint)
	MountSliceIns.Mutex.Lock()
	delete(MountSliceIns.DriversMap, fsPath)
	MountSliceIns.Mutex.Unlock()
	return nil
}

func (o *LinuxDiskOperator) IsMounted(fsPath string) (mounted bool, err error) {
	fsPath = bytes.NewBufferString("//" + fsPath).String()
	MountSliceIns.Mutex.RLock()
	defer MountSliceIns.Mutex.RUnlock()
	if _, ok := MountSliceIns.DriversMap[fsPath]; ok {
		return true, nil
	}
	return false, err
}

func (o *LinuxDiskOperator) CheckMountHealth(fsPath, clientIp string) (err error) {
	fsPath = bytes.NewBufferString("//" + clientIp + fsPath).String()
	MountSliceIns.Mutex.RLock()
	defer MountSliceIns.Mutex.RUnlock()
	mountPoint, _ := MountSliceIns.DriversMap[fsPath]
	_, err = os.ReadDir(mountPoint)
	return
}

func (o *LinuxDiskOperator) ListMount() (bytes []byte, err error) {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	cmd := exec.CommandContext(ctx, "bash", "-c", "cat /proc/mounts | grep cifs | awk '{print $1,$2}'")
	output, err := cmd.CombinedOutput()
	if err != nil {
		return nil, err
	}
	return output, nil
}

func (o *LinuxDiskOperator) ParseDf(output []byte) {
	MountSliceIns.DriversMap = make(map[string]string)
	lines := bytes.Split(output, []byte{'\n'})
	for _, line := range lines {
		if len(line) == 0 {
			continue
		}
		// 分割字段（自动处理多空格）
		fields := strings.SplitN(string(line), " ", 2)
		MountSliceIns.DriversMap[fields[0]] = fields[1]
	}
}
